﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.DB;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System.IO;
using System.Runtime.InteropServices;

namespace RevitToOSG
{
    [Transaction(TransactionMode.Manual)]

    public class Converter:IExternalCommand
    {
        [DllImport("OSGConverter.dll")]
        private static extern int OSGConverter(int Id, string className, string elemName, int v_cnt,int cur_cnt, IntPtr p_cnt,IntPtr points, IntPtr vertices, IntPtr normals, IntPtr lineColors);
        [DllImport("OSGConverter.dll")]
        private static extern int WriteOSGFile(string path);
        [DllImport("OSGConverter.dll")]
        private static extern void clear();
        [DllImport("OSGConverter.dll")]
        private static extern void set_Material(int Id, double transparancy);

        Result IExternalCommand.Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Document doc = uidoc.Document;

            //Selection selection = uidoc.Selection;
            //Reference r = selection.PickObject(ObjectType.Element);
            //Element element = doc.GetElement(r);

            //Material material = doc.GetElement(element.GetMaterialIds(false).First()) as Material;
            //if(material == null)
            //{
            //    TaskDialog.Show("warning!", "No material!");
            //    return Result.Failed;
            //}
            //string color = material.Color.Red.ToString() + " " + material.Color.Green.ToString() + " " + material.Color.Blue.ToString();
            //TaskDialog.Show("colorRGB", color);

            //Wall wall = element as Wall;

            //获取所有构件信息
            FilteredElementCollector collectorAll = new FilteredElementCollector(doc);
            collectorAll.WherePasses(new ElementIsElementTypeFilter(true));
            //collectorAll.WherePasses(new LogicalOrFilter(new ElementIsElementTypeFilter(false), new ElementIsElementTypeFilter(true)));
            //List<ElementClassFilter> filters = new List<ElementClassFilter>();
            //filters.Add(new ElementClassFilter(typeof(FamilyInstance)));
            //filters.Add(new ElementClassFilter(typeof(HostObject)));
            //filters.Add(new ElementClassFilter(typeof(Wall)));
            //filters.Add(new ElementClassFilter(typeof(Stairs)));
            //ElementMulticlassFilter filter = new ElementMulticlassFilter()
            IList<Element> elems = collectorAll.ToElements();
            //TaskDialog.Show("revit", elems.Count().ToString());

            //过滤视图中所有构件
            //FilteredElementCollector temc = new FilteredElementCollector(doc, doc.ActiveView.Id);
            //temc.WhereElementIsNotElementType();
            StreamWriter sw = new StreamWriter(@"C:\Users\Ichi\Desktop\Geometry.txt");
            StreamWriter sw2 = new StreamWriter(@"C:\Users\Ichi\Desktop\Curves.txt");
            foreach (Element element in commandData.Application.ActiveUIDocument.Selection.PickElementsByRectangle())
            //foreach (Element element in elems)
            {
                if (element.GetTypeId() != null)
                {
                    //获取几何信息
                    Options option = commandData.Application.Application.Create.NewGeometryOptions();
                    GeometryElement geometryElement = element.get_Geometry(option);

                    //剔除不能转换为geometryElement的元素
                    if (geometryElement == null)
                    {
                        continue;
                    }

                    //获取element的所有solid和curve
                    List<Curve> curves = new List<Curve>();
                    List<Mesh> meshes = new List<Mesh>();
                    List<Solid> solids = new List<Solid>();
                    getGeometry.AddCurvesAndSolids(geometryElement, curves, meshes, solids);

                    //TaskDialog.Show("Num of Solids", solids.Count().ToString());

                    //剔除无几何构造的元素
                    if (curves.Count() == 0 && solids.Count() == 0)
                    {
                        continue;
                    }

                    /********************************************几何信息的获取********************************************/

                    //获取curve所有顶点信息
                    List<XYZ> pList = new List<XYZ>();  //创建二维表，记录每根曲线所有点的信息
                    int cur_cnt = curves.Count(); //记录曲线数量
                    int[] pNum = new int[cur_cnt];  //创建数组记录每根曲线中点的数目
                    for (int k = 0; k < cur_cnt; ++k)
                    {
                        pNum[k]=getGeometry.AddVerticesOfCurve(curves[k], pList);   //获取所有曲线的点,并将每根曲线点的个数存储
                    }

                    double[] p = new double[3 * pList.Count()];  // 逐点存储xyz值
                    for (int k = 0; k < pList.Count();++k)
                    {
                        p[3 * k] = 0.3048 * pList[k].X;
                        p[3 * k + 1] = 0.3048 * pList[k].Y;
                        p[3 * k + 2] = 0.3048 * pList[k].Z;
                        string str2 = p[3 * k].ToString() + " " + p[3 * k + 1].ToString() + " " + p[3 * k + 2].ToString();
                        sw2.WriteLine(str2);
                    }

                    IntPtr p_cnt = Marshal.UnsafeAddrOfPinnedArrayElement(pNum, 0);
                    IntPtr points = Marshal.UnsafeAddrOfPinnedArrayElement(p, 0);



                    //获取solid所有顶点和法线信息
                    List<XYZ> vertices = new List<XYZ>();
                    List<XYZ> normals = new List<XYZ>();

                    //获取所有solid顶点和法线
                    foreach (Solid solid in solids)
                    {
                        getGeometry.AddVerticesAndNormalsOfSolid(solid, vertices, normals);
                    }

                    //获取所有mesh顶点和法线
                    foreach (Mesh mesh in meshes)
                    {
                        getGeometry.AddVerticesAndNormalsOfMesh(mesh, vertices, normals);
                    }


                    /**************************************获取Id等信息*********************************************/
                    //获取顶点个数
                    int v_cnt = vertices.Count();
                
                    //获取构件的ID
                    ElementId elementId = element.Id;
                    int Id = elementId.IntegerValue;

                    //获取构件类型
                    string className = element.Category.Name;

                    //获取构件名称
                    string elemName = element.Name;

                    string str = Id.ToString() + ' ' + className + ' ' + elemName + ' ' + v_cnt.ToString();
                    sw.WriteLine(str);

                    //暂时不写入无顶点的元素
                    if (v_cnt == 0)
                    {
                        continue;
                    }

                    //获取所有顶点坐标
                    double[] v = new double[v_cnt * 3];

                    //获取所有法线坐标
                    double[] n = new double[v_cnt * 3];

                    int i, j;
                    i = j = 0;

                    foreach (XYZ vertex in vertices)
                    {
                        // 将三角面片顶点坐标写入文件
                        v[i] = 0.3048 * vertex.X;
                        v[i + 1] = 0.3048 * vertex.Y;
                        v[i + 2] = 0.3048 * vertex.Z;
                        i += 3;
                    }

                    foreach (XYZ normal in normals)
                    {
                        //将顶点法线写入文件
                        n[j] = normal.X;
                        n[j + 1] = normal.Y;
                        n[j + 2] = normal.Z;
                        j += 3;
                    }

                    IntPtr vers = Marshal.UnsafeAddrOfPinnedArrayElement(v, 0);
                    IntPtr norms = Marshal.UnsafeAddrOfPinnedArrayElement(n, 0);


                    /**************************************获取颜色、材质信息*********************************************/
                    int[] colors = new int[3 * cur_cnt];
                    for(int k=0;k<cur_cnt;++k)
                    {
                        Color color = getGeometry.getMaterialOfCurve(doc, curves[k]).LineColor;
                        colors[3 * k] = color.Red;
                        colors[3 * k+1] = color.Green;
                        colors[3 * k+2] = color.Blue;
                    }

                    IntPtr lineColors= Marshal.UnsafeAddrOfPinnedArrayElement(colors, 0);

                    /**************************************转换成osg文件以及后期处理*********************************************/
                    //调用函数OSGConverter，传入参数并将构件转换为Osg格式
                    OSGConverter(Id, className, elemName, v_cnt,cur_cnt, p_cnt, points, vers, norms,lineColors);


                    //对不同类型的元素设置材质属性
                    if (className == "房间")
                    {
                        set_Material(Id, 1);
                    }

                    
                }
            }

            TaskDialog.Show("Children_Cnt", WriteOSGFile("C:\\Users\\Ichi\\Desktop\\osg\\Model.osg").ToString());
            //TaskDialog.Show("Note", k.ToString());
            TaskDialog.Show("Note", "Succeed!");
            sw.Close();
            sw2.Close();
            clear();
            return Result.Succeeded;
        }
    }
}

